package com.vvv.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vvv.shortlink.common.convention.excepton.ClientException;
import com.vvv.shortlink.common.convention.excepton.ServiceException;
import com.vvv.shortlink.common.convention.result.Result;
import com.vvv.shortlink.admin.dao.entity.GroupDO;
import com.vvv.shortlink.admin.dao.mapper.GroupMapper;
import com.vvv.shortlink.admin.dto.req.ShortLinkGroupSaveReqDTO;
import com.vvv.shortlink.admin.dto.req.ShortLinkGroupSortReqDTO;
import com.vvv.shortlink.admin.dto.req.ShortLinkGroupUpdateReqDTO;
import com.vvv.shortlink.admin.dto.resp.LinkGroupRespDTO;
import com.vvv.shortlink.admin.remote.ShortLinkProjectClient;
import com.vvv.shortlink.admin.remote.dto.resp.LinkGroupCountRespDTO;
import com.vvv.shortlink.admin.service.GroupService;
import com.vvv.shortlink.common.toolkit.RandomStringUtil;
import com.vvv.shortlink.common.toolkit.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.vvv.shortlink.admin.constant.RedisConstant.LOCK_CREATE_GROUP_KEY;

/**
* @author Dell
* @description 针对表【t_group】的数据库操作Service实现
* @createDate 2024-02-01 10:48:53
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO>
    implements GroupService {

    private final ShortLinkProjectClient shortLinkProjectClient;
    private final RedissonClient redissonClient;

    @Value("${short-link.group.max-num}")
    private Integer groupMaxNum;

    @Override
    public void saveGroup(ShortLinkGroupSaveReqDTO requestParam) {
       this.saveGroup(UserContext.getUserId(), requestParam);
    }

    @Override
    public void saveGroup(Long userId, ShortLinkGroupSaveReqDTO requestParam) {
        String name = requestParam.getName();
        if (StringUtils.isEmpty(name)) {
            throw new ClientException("分组名不能为空");
        }
        RLock lock = redissonClient.getLock(LOCK_CREATE_GROUP_KEY + userId);
        boolean isLock = lock.tryLock();
        if (!isLock) {
            throw new ClientException("分组创建失败，请稍后重试");
        }
        try {
            // 查询该用户下的分组数是否大于 groupMaxNum
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUserId, userId);
            Long count = baseMapper.selectCount(queryWrapper);
            if (count != null && count > groupMaxNum) {
                throw new ClientException("已超出最大分组数：%d" +  groupMaxNum);
            }
            // 生成唯一gid
            String gid = this.generateUniqueGid(userId);
            GroupDO groupDO = GroupDO.builder()
                    .gid(gid)
                    .userId(userId)
                    .name(name)
                    .build();
            baseMapper.insert(groupDO);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<LinkGroupRespDTO> listGroup() {
        Long userId = UserContext.getUserId();
        if (userId == null) {
            return null;
        }
        // 1. 获取用户id下的全部分组
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getUserId, userId)
                .orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);
        List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);
        List<LinkGroupRespDTO> result = BeanUtil.copyToList(groupDOList, LinkGroupRespDTO.class);

        // 2. 远程调用获取该用户各分组下面的短链数量
        Result<List<LinkGroupCountRespDTO>> remoteResult = shortLinkProjectClient.listShortLinkGroupCount(userId);
        Map<String, List<LinkGroupCountRespDTO>> remoteResultMap = remoteResult.getData()
                .stream()
                .collect(Collectors.groupingBy(LinkGroupCountRespDTO::getGid));
        result.forEach(each -> {
            List<LinkGroupCountRespDTO> tempList = remoteResultMap.get(each.getGid());
            if (CollectionUtil.isEmpty(tempList)) {
                each.setShortLinkCount(0);
            } else {
                each.setShortLinkCount(tempList.get(0).getShortLinkCount());
            }
        });
        return result;
    }

    @Override
    public void updateGroup(ShortLinkGroupUpdateReqDTO requestParam) {
        String gid = requestParam.getGid();
        String name = requestParam.getName();
        if (StringUtils.isAnyEmpty(gid, name)) {
            throw new ClientException("参数不能为空");
        }
        Long userId = UserContext.getUserId();
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getUserId, userId)
                .eq(GroupDO::getGid, gid);
        GroupDO groupDO = new GroupDO();
        groupDO.setName(name);
        baseMapper.update(groupDO,updateWrapper);
    }

    @Override
    public void deleteGroup(String gid) {
        Long userId = UserContext.getUserId();
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUserId, userId);
        baseMapper.delete(queryWrapper);
    }

    @Override
    public void sortGroup(List<ShortLinkGroupSortReqDTO> requestParam) {
        Long userId = UserContext.getUserId();
        for (ShortLinkGroupSortReqDTO each : requestParam) {
            LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                    .eq(GroupDO::getSortOrder, each.getSortOrder());
            GroupDO groupDO = GroupDO.builder()
                    .gid(each.getGid())
                    .userId(userId)
                    .build();
            baseMapper.update(groupDO, updateWrapper);
        }
    }

    /**
     * 生成userId下唯一的gid
     * 因为userId为t_group的分片键，所以如果要生成全局唯一gid要
     * @return
     */
    private String generateUniqueGid(Long userId) {
        int maxRetrySize = 3;
        int count = 0;
        while (count++ < maxRetrySize) {
            String gid = RandomStringUtil.generateRandom();
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUserId, userId)
                    .eq(GroupDO::getGid, gid);
            if (baseMapper.selectCount(queryWrapper) == 0) {
                // 创建成功
                return gid;
            }
            // 创建失败
            log.warn("短链接分组：gid生成冲突，重试次数: {}", count);
        }
        throw new ServiceException("短链接分组创建失败");
    }
}




